package hikari.gui.panels;

import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.guicomponents.draggable_tree.DraggableTree;
import hikari.gui.guicomponents.draggable_tree.DraggableTreeChangeParentEvent;
import hikari.managers.Manager3D;
import hikari.managers.SelectionManager;
import hikari.objects3d.Basic3dObject;
import java.util.Enumeration;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import org.swig.jirr.ISceneNode;

/**
 * Created with IntelliJ IDEA. User: Dmitry Date: 24.11.12 Time: 21:48
 */
@WiringComponent(singleton = true, lazy = true)
public class HierarchyTree extends DraggableTree {

    @Wire
    private Manager3D manager3D;
    @Wire
    private SelectionManager selectionManager;
    private boolean processSelectionEvents = true;

    private HierarchyTree() {
        super("Scene");
    }

    @PostWiring
    private void init() {
        selectionManager.addSelectionEvent(new SelectionManager.SelectionEvent() {
            @Override
            public void onSelected(Basic3dObject currentSelection, Basic3dObject newSelection) {
                if (processSelectionEvents) {
                    processSelectionEvents = false;
                    DefaultMutableTreeNode node = searchNode(newSelection);
                    if (node != null) {
                        selectNode(node);
                    } else {
                        selectNode((TreeNode) model.getRoot());
                    }

                    processSelectionEvents = true;
                }
            }

            @Override
            public boolean isCanSelect(Basic3dObject currentSelection, Basic3dObject newSelection) {
                return true;
            }
        });
        addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                if (processSelectionEvents) {
                    processSelectionEvents = false;
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
                    if (node.getUserObject() instanceof Basic3dObject) {
                        selectionManager.select((Basic3dObject) node.getUserObject());
                    } else {
                        selectionManager.select(null);
                    }
                    processSelectionEvents = true;
                }
            }
        });
        setOnChangeParentEvent(new DraggableTreeChangeParentEvent() {
            @Override
            public boolean changeParent(TreeNode previousParent, TreeNode newParent, TreeNode child) {
                DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) child;
                DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) newParent;
                changeParentNode(childNode, parentNode, false);
                return true;
            }
        });
    }

    public void changeParentNode(Basic3dObject childNode, Basic3dObject parentNode, boolean changeParentInTree) {
        DefaultMutableTreeNode parentTreeNode = searchNode(parentNode);
        DefaultMutableTreeNode childTreeNode = searchNode(childNode);
        changeParentNode(childTreeNode, parentTreeNode, changeParentInTree);
    }

    public void changeParentNode(DefaultMutableTreeNode childNode, DefaultMutableTreeNode parentNode, boolean changeParentInTree) {
        if (childNode.getUserObject() instanceof Basic3dObject) {
            Basic3dObject childObject = (Basic3dObject) childNode.getUserObject();
            if (parentNode.getUserObject() instanceof Basic3dObject) {
                Basic3dObject parentObject = (Basic3dObject) parentNode.getUserObject();
                childObject.getMeshSceneNode().setDynamicParent(parentObject.getMeshSceneNode());
            } else {
                childObject.getMeshSceneNode().setDynamicParent(manager3D.getSceneManager().getRootSceneNode());
            }
            if (changeParentInTree) {
                changeParentingOnlyInTree(childNode, parentNode);
            }
        }
    }

    public void changeParentingOnlyInTree(Basic3dObject childNode, Basic3dObject parentNode) {
        DefaultMutableTreeNode parentTreeNode = searchNode(parentNode);
        DefaultMutableTreeNode childTreeNode = searchNode(childNode);
        changeParentingOnlyInTree(childTreeNode, parentTreeNode);
    }

    public void changeParentingOnlyInTree(DefaultMutableTreeNode childNode, DefaultMutableTreeNode parentNode) {
        model.removeNodeFromParent(childNode);
        model.insertNodeInto(childNode, parentNode, parentNode.getChildCount());
    }

    public DefaultMutableTreeNode getRoot() {
        return (DefaultMutableTreeNode) model.getRoot();
    }

    public void refreshObject(Basic3dObject object) {
        TreeNode node = searchNode(object);
        if (node != null) {
            ((DefaultTreeModel) getModel()).nodeChanged(node);
        }
    }

    public void AddObject(Basic3dObject object) {
        DefaultMutableTreeNode node = new DefaultMutableTreeNode(object);
        model.insertNodeInto(node, getRoot(), getRoot().getChildCount());
    }

    public static interface HierarchyTreeNodeEnumarator {

        boolean onNode(Basic3dObject node);
    }

    /**
     * If method returns false, looping will stop
     */
    public void forEachNodes(HierarchyTreeNodeEnumarator enumerator) {
        DefaultMutableTreeNode node;
        Enumeration e = ((DefaultMutableTreeNode) model.getRoot()).breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            node = (DefaultMutableTreeNode) e.nextElement();
            if (node.getUserObject() instanceof Basic3dObject) {
                if (!enumerator.onNode((Basic3dObject) node.getUserObject())) {
                    break;
                }
            }
        }
    }

    public DefaultMutableTreeNode searchNode(ISceneNode innerNode) {
        DefaultMutableTreeNode node;
        Enumeration e = ((DefaultMutableTreeNode) model.getRoot()).breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            node = (DefaultMutableTreeNode) e.nextElement();
            if (node.getUserObject() instanceof Basic3dObject) {
                if (((Basic3dObject) node.getUserObject()).getMeshSceneNode().equals(innerNode)) {
                    return node;
                }
            }
        }

        return null;
    }

    public DefaultMutableTreeNode searchNode(Basic3dObject object) {
        DefaultMutableTreeNode node;
        Enumeration e = ((DefaultMutableTreeNode) model.getRoot()).breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            node = (DefaultMutableTreeNode) e.nextElement();
            if (node.getUserObject() instanceof Basic3dObject) {
                if (((Basic3dObject) node.getUserObject()).equals(object)) {
                    return node;
                }
            }
        }

        return null;
    }

    public void removeObject(Basic3dObject object) {
        DefaultMutableTreeNode node = searchNode(object);
        model.removeNodeFromParent(node);
    }

    public void removeAllChilds() {
        ((DefaultMutableTreeNode) model.getRoot()).removeAllChildren();
        model.nodeStructureChanged((DefaultMutableTreeNode) model.getRoot());
    }
}
